﻿using Furion.DatabaseAccessor;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UpperComputer.Core.Entities;
using UpperComputer.Core.Extensions;
using YFurion.Application;
using YFurion.Application.EFCore.Extensions;
using YFurion.Extensions;
using YFurion.Utils;

namespace UpperComputer.Application
{
    public class BaseUserService : EFCoreApplicationService<BaseUser, BaseUserResultDto, long, BaseUserResultRequestDto, BaseUserCreateDto, BaseUserDto>, IBaseUserService
    {
        private const string DEFAULT_PASSWORD = "123456";
        private const string ROLE_CODES_CACHE_KEY_SUFFIX = "#RoleCodes";

        private readonly IBaseRoleUserService _roleUserService;
        private readonly IDFSManager _dfsManager;
        public BaseUserService(
            IBaseRoleUserService roleUserService,
            IDFSManager dfsManager
         )
        {
            _roleUserService = roleUserService;
            _dfsManager = dfsManager;
        }

        /// <summary>
        /// 获取两张表的所有数据
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task<PagedResultDto<BaseUserResultDto>> GetAll(BaseUserResultRequestDto input, CancellationToken cancellationToken = default)
        {
            PagedResultDto<BaseUserResultDto> pagedResultDto = await base.GetAll(input, cancellationToken);
            IReadOnlyList<BaseUserResultDto> baseUserResultDtos = pagedResultDto.Items;
            int count = baseUserResultDtos.Count;
            if (count > 0)
            {
                List<string> accounts = new List<string>();
                foreach (BaseUserResultDto baseUserResultDto in baseUserResultDtos)
                {
                    accounts.Add(baseUserResultDto.Account);
                }
                ICollection<BaseRoleUserDto> baseRoleUserResultDtos = await _roleUserService.GetListWithAccounts(accounts, cancellationToken);
                for (int i = 0; i < count; ++i)
                {
                    BaseUserResultDto baseUserResultDto = baseUserResultDtos[i];
                    baseUserResultDto.BaseRoleUser = baseRoleUserResultDtos.Where(bru => bru.Account.Equals(baseUserResultDto.Account)).ToList();
                }
            }

            return pagedResultDto;
        }

        #region  模糊查询 
        protected override IQueryable<BaseUser> CreateFilteredQuery(BaseUserResultRequestDto input)
        {
            //根据查询条件进行查询
            IQueryable<BaseUser> query = base.CreateFilteredQuery(input)
                .WhereIf(!input.Account.IsNullOrWhiteSpace(), bu => bu.Account.StartsWith(input.Account))
                .WhereIf(!input.DisplayName.IsNullOrWhiteSpace(), bu => bu.DisplayName.StartsWith(input.DisplayName));
            return query;
        }
        #endregion

        #region 获取单条数据 
        public override async Task<BaseUserResultDto> Get(long id, CancellationToken cancellationToken = default)
        {
            BaseUserResultDto baseUserResultDto = await base.Get(id, cancellationToken);
            baseUserResultDto.BaseRoleUser = await _roleUserService.GetListWithAccount(baseUserResultDto.Account, cancellationToken);
            return baseUserResultDto;
        }
        #endregion

        public async Task<string[]> GetRoleCodesCache(BaseUserDto input, CancellationToken cancellationToken = default)
        {
            string[] roleCodes = await GetOrCreateCacheAsync($"{new BaseUserDto { Id = input.Id }}{ROLE_CODES_CACHE_KEY_SUFFIX}", async cancellationToken =>
              {
                  ICollection<BaseRoleUserDto> baseRoleUserDtos = await MSRepository.Slave1<BaseRoleUser>().Where(bru => bru.Account.Equals(input.Account)).ProjectToType<BaseRoleUserDto>().ToListAsync(cancellationToken);
                  return GetRoleCodes(baseRoleUserDtos);
              }, cancellationToken, DistributedCache.GetDefaultDistributedCacheEntryOptions());
            return roleCodes;
        }

        #region  新增 
        public override async Task<BaseUserResultDto> Post(BaseUserCreateDto input, CancellationToken cancellationToken = default)
        {
            BaseUser user = await MasterRepository.AsQueryable(bu => bu.Account.Equals(input.Account), ignoreQueryFilters: true).FirstOrDefaultAsync(cancellationToken);
            if (user != null && !user.IsDeleted)
            {
                throw Oh("用户已存在");
            }

            await _roleUserService.DeleteAsync(input.Account, cancellationToken);

            foreach (BaseRoleUserDto item in input.BaseRoleUser)
            {
                item.Id = YIDGenerator.NextId();
            }

            BaseUserResultDto baseUserResultDto = null;
            if (user == null)
            {
                string pwd = GetMD5Password(DEFAULT_PASSWORD);
                input.Password = pwd;
                if (input.File != null)
                {
                    string fileId = await _dfsManager.UploadImageAsync(input.File, 1024 * 1024 * 2, cancellationToken);
                    input.Photo = fileId;
                }
                baseUserResultDto = await base.Post(input, cancellationToken);
            }
            else
            {
                input.Id = user.Id;
                if (input.File != null)
                {
                    string fileId = await _dfsManager.UploadImageAsync(input.File, 1024 * 1024 * 2, cancellationToken);
                    if (!input.Photo.IsNullOrWhiteSpace())
                    {
                        await _dfsManager.RemoveImageAsync(input.Photo, cancellationToken);
                    }
                    input.Photo = fileId;
                }
                user.IsDeleted = false;
                baseUserResultDto = await UpdateAsync(input, user, cancellationToken);
            }
            return baseUserResultDto;
        }
        #endregion

        #region 修改
        public override async Task<BaseUserResultDto> Put(BaseUserDto input, CancellationToken cancellationToken = default)
        {
            BaseUser user = await MasterRepository.FindOrDefaultAsync(input.Id, cancellationToken);
            if (user == null)
            {
                throw Oh("用户不存在");
            }
            await CheckSA(input.Id, cancellationToken, false);
            BaseUser serachUser = await MasterRepository.FirstOrDefaultAsync(bu => !bu.Id.Equals(input.Id) && bu.Account.Equals(input.Account), cancellationToken: cancellationToken);
            if (serachUser != null)
            {
                throw Oh("用户已存在");
            }
            if (input.File != null)
            {
                string fileId = await _dfsManager.UploadImageAsync(input.File, 1024 * 1024 * 2, cancellationToken);
                if (!input.Photo.IsNullOrWhiteSpace())
                {
                    await _dfsManager.RemoveImageAsync(input.Photo, cancellationToken);
                }
                input.Photo = fileId;
            }
            await _roleUserService.DeleteAsync(input.Account, cancellationToken);
            foreach (BaseRoleUserDto item in input.BaseRoleUser)
            {
                item.Id = YIDGenerator.NextId();
            }
            BaseUserResultDto baseUserResultDto = await UpdateAsync(input, user, cancellationToken);
            await SetCacheAsync($"{input}{ROLE_CODES_CACHE_KEY_SUFFIX}", GetRoleCodes(baseUserResultDto.BaseRoleUser), cancellationToken, DistributedCache.GetDefaultDistributedCacheEntryOptions());
            return baseUserResultDto;
        }

        #endregion

        #region 编辑
        public async Task<BaseUserResultDto> EditUser(BaseUserDto input, CancellationToken cancellationToken = default)
        {
            BaseUserResultDto baseUserResultDto = await base.Put(input, cancellationToken);
            return baseUserResultDto;
        }
        #endregion

        #region 重置密码 
        public async Task<BaseUserResultDto> Recover(EntityDto input, CancellationToken cancellationToken = default)
        {
            await CheckSA(input.Id, cancellationToken);
            BaseUser user = await GetEntity(input.Id, cancellationToken);
            string pwd = GetMD5Password(DEFAULT_PASSWORD);
            user.Password = pwd;
            BaseUserResultDto baseUserResultDto = await UpdateAsync(user, cancellationToken);
            return baseUserResultDto;
        }
        #endregion

        #region 启用按钮 
        public async Task<BaseUserResultDto> Enable(EntityDto input, CancellationToken cancellationToken = default)
        {
            await CheckSA(input.Id, cancellationToken);
            BaseUser user = await GetEntity(input.Id, cancellationToken);
            user.IsActive = true;
            BaseUserResultDto baseUserResultDto = await UpdateAsync(user, cancellationToken);
            return baseUserResultDto;
        }
        #endregion

        #region 禁用按钮 
        public async Task<BaseUserResultDto> Disable(EntityDto input, CancellationToken cancellationToken = default)
        {
            await CheckSA(input.Id, cancellationToken);
            BaseUser user = await GetEntity(input.Id, cancellationToken);
            user.IsActive = false;
            BaseUserResultDto baseUserResultDto = await UpdateAsync(user, cancellationToken);
            return baseUserResultDto;
        }
        #endregion

        #region 修改密码 
        public async Task<BaseUserResultDto> ChangePassword(ChangePasswordDto input, CancellationToken cancellationToken = default)
        {
            BaseUser user = await GetEntity(input.Id, cancellationToken);
            string md5Password = input.OldPassword.GetMD5PrefixPassword();
            if (user.Password != md5Password)
            {
                throw Oh("原始密码不正确");
            }
            user.Password = input.NewPassword.GetMD5PrefixPassword();
            BaseUserResultDto baseUserResultDto = await UpdateAsync(user, cancellationToken);
            return baseUserResultDto;
        }
        #endregion

        #region  修改图片
        public async Task<string> UploadPhoto(IFormFile file, long id, CancellationToken cancellationToken = default)
        {
            string fileId = await _dfsManager.UploadImageAsync(file, 1024 * 1024 * 2, cancellationToken);
            BaseUser user = await GetEntity(id, cancellationToken);
            string oldPhoto = user.Photo;
            user.Photo = fileId;
            await UpdateAsync(user, cancellationToken);
            if (!oldPhoto.IsNullOrWhiteSpace())
            {
                await _dfsManager.RemoveImageAsync(oldPhoto, cancellationToken);
            }
            return fileId;
        }
        #endregion

        #region 删除图片
        public async Task DeletePhoto(BaseUserDto input, CancellationToken cancellationToken = default)
        {
            await _dfsManager.RemoveImageAsync(input.Photo, cancellationToken);
            if (input.Id > 0)
            {
                input.Photo = "";
                await Put(input, cancellationToken);
            }
        }
        #endregion

        #region 删除
        public override async Task Delete(long id, CancellationToken cancellationToken = default)
        {
            await CheckSA(id, cancellationToken);
            await base.Delete(id, cancellationToken);
            await RemoveCacheAsync($"{new BaseUserDto { Id = id }}{ROLE_CODES_CACHE_KEY_SUFFIX}", cancellationToken);
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id">用户ID</param>
        /// <param name="filteredSA">是否过滤超级管理员</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// 
        private async Task CheckSA(long id, CancellationToken cancellationToken, bool filteredSA = true)
        {
            BaseUserResultDto userResultDto = await GetCacheById(id, cancellationToken);
            bool invalid = userResultDto.Account.Equals("Admin");
            if (!filteredSA)
            {
                invalid &= !YSession.SubjectId.Equals(userResultDto.Id);
            }
            if (invalid)
            {
                throw Oh("不能更改或删除超级管理员");
            }
        }

        private string GetMD5Password(string value)
        {
            return value.GetMD5SuffixPassword().GetMD5PrefixPassword();
        }

        private string[] GetRoleCodes(ICollection<BaseRoleUserDto> baseRoleUserResultDtos)
        {
            string[] roleCodes = new string[baseRoleUserResultDtos.Count];
            for (int i = 0; i < baseRoleUserResultDtos.Count; ++i)
            {
                roleCodes[i] = baseRoleUserResultDtos.ElementAt(i).RoleCode;
            }
            return roleCodes;
        }
        #endregion

        #region 根据id查询
        public async Task GetUserCount(long roleId, CancellationToken cancellationToken = default)
        {
            //根据查询条件进行查询
            BaseRole baseRole = await MSRepository.Master<BaseRole>().FirstOrDefaultAsync(br => br.Id.Equals(roleId), cancellationToken: cancellationToken);
            if (baseRole != null)
            {
                BaseRoleUser baseRoleUser = await MSRepository.Master<BaseRoleUser>().FirstOrDefaultAsync(bru => bru.RoleCode.Equals(baseRole.Code), cancellationToken: cancellationToken);
                if (baseRoleUser != null)
                {
                    throw Oh("角色下已经存在用户，请清空角色下的用户再删除");
                }
            }
        }
        #endregion
    }
}